#ifndef _FILESTREAM_HPP_
#define _FILESTREAM_HPP_

#include "jhdefs.hpp"
#include "types.hpp"
#include "platform.hpp"
#include "stream/streambuf.hpp"
#include <string>
#include <istream>
#include <ostream>

STREAM_NAMESPACE_BEGIN

#ifdef JH_OS_WINDOWS
#include <Windows.h>

class CFileStreamBuf : public BufferedBidirectionalStreamBuf
{
public:
	CFileStreamBuf();
	~CFileStreamBuf();

	void open(const std::string& path, std::ios::openmode mode);
	bool close();

	std::streampos seekoff(std::streamoff off, std::ios::seekdir dir, std::ios::openmode mode = std::ios::in | std::ios::out);
	std::streampos seekpos(std::streampos pos, std::ios::openmode mode = std::ios::in | std::ios::out);

protected:
	enum
	{
		BUFFER_SIZE = 4096
	};

	int readFromDevice(char* buffer, std::streamsize length);
	int writeToDevice(const char* buffer, std::streamsize length);

private:
	std::string m_path;
	HANDLE		m_handle;
	UInt64		m_pos;
};

#elif defined JH_OS_LINUX

class CFileStreamBuf : public BufferedBidirectionalStreamBuf
{
public:
	CFileStreamBuf();
	~CFileStreamBuf();

	void open(const std::string& path, std::ios::openmode mode);
	bool close();

	std::streampos seekoff(std::streamoff off, std::ios::seekdir dir, std::ios::openmode mode = std::ios::in | std::ios::out);
	std::streampos seekpos(std::streampos pos, std::ios::openmode mode = std::ios::in | std::ios::out);

protected:
	enum
	{
		BUFFER_SIZE = 4096
	};

	int readFromDevice(char* buffer, std::streamsize length);
	int writeToDevice(const char* buffer, std::streamsize length);

private:
	std::string		m_path;
	int				m_fd;
	std::streamoff m_pos;
};

#endif


class CFileIOS : public virtual std::ios
{
public:
	CFileIOS(std::ios::openmode defaultMode);
	~CFileIOS();

	void open(const std::string& path, std::ios::openmode mode);
	void close();

	CFileStreamBuf* rdbuf();

protected:
	CFileStreamBuf		m_buf;
	std::ios::openmode	m_defaultMode;
};

class CFileInputStream : public CFileIOS, public std::istream
{
public:
	CFileInputStream();
	CFileInputStream(const std::string& path, std::ios::openmode mode = std::ios::in);
	~CFileInputStream();
};


class CFileOutputStream : public CFileIOS, public std::ostream
{
public:
	CFileOutputStream();
	CFileOutputStream(const std::string& path, std::ios::openmode mode = std::ios::out | std::ios::trunc);
	~CFileOutputStream();
};


class CFileStream : public CFileIOS, public std::iostream
{
public:
	CFileStream();
	CFileStream(const std::string& path, std::ios::openmode mode = std::ios::out | std::ios::in);
	~CFileStream();
};

STREAM_NAMESPACE_END

#endif
